package http_proxy

import (
	"net/http"
	"sync/atomic"
	"time"

	"github.com/gin-gonic/gin"
)

const (
	requestWaiting = iota
	requestFinished
)

type (
	//serviceRoute rpc 映射http方法路由代理
	serviceRoute struct {
		hp          *HttpProxy    //http proxy 指针
		serviceUuid uint64        //服务uid
		methodId    uint32        //方法id
		service     string        //方法名
		method      string        //函数名
		timeout     time.Duration //超时时间
	}

	requestInfo struct {
		id      uint32             // call id
		status  int32              // 请求状态
		uid     uint64             // 服务uid
		mid     uint32             // 方法uid
		service string             // 服务名字
		method  string             // 方法id
		body    []byte             // 请求包体
		done    chan *responseInfo // 回包等待
	}

	responseInfo struct {
		status int // http response 状态
		err    error
		body   string
	}
)

func makeHttpCall(uuid uint64, methodId uint32, service string, method string) *requestInfo {
	return &requestInfo{
		uid:     uuid,
		status:  requestWaiting,
		mid:     methodId,
		service: service,
		method:  method,
		done:    make(chan *responseInfo, 1),
	}
}

func (s *serviceRoute) onRequest(c *gin.Context) {
	// http proxy 状态检查
	if s.hp == nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"error": "No http proxy is available",
		})
		return
	}

	// 检查http proxy 的状态, 如果不是可以服务状态，暂停服务
	if !s.hp.IsResolved() {
		c.JSON(http.StatusInternalServerError, gin.H{
			"error": "No http proxy is available",
		})
		return
	}

	// 组装rpc调用头部信息
	call := makeHttpCall(s.serviceUuid, s.methodId, s.service, s.method)

	// 获取 body
	var err error
	call.body, err = c.GetRawData()
	if call.body == nil || err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"message": "Invalid parameters",
			"err":     err,
		})
		return
	}

	// 检查body的长度, 长度为0, 时候需要拦截，否则会崩溃
	if len(call.body) == 0 {
		c.JSON(http.StatusBadRequest, gin.H{
			"message": "Invalid parameters length",
		})
		return
	}

	// 发送到主协程
	s.hp.onRequest(call)

	// 启动定时器
	timer := time.NewTimer(s.timeout * time.Millisecond)

	// 总是清理定时器
	defer timer.Stop()

	// 阻塞等待调用返回
	select {
	case resp, ok := <-call.done:
		if !ok {
			// 通道关闭
			c.JSON(http.StatusInternalServerError, gin.H{
				"error": "service has been shutdown",
			})
		}

		if resp == nil {
			//管道关闭
			c.JSON(http.StatusInternalServerError, gin.H{
				"error": "service has been closed !",
			})
			return
		}

		if resp.err == nil {
			c.JSON(resp.status, resp.body)
		} else {
			c.JSON(resp.status, gin.H{
				"error": resp.err.Error(),
			})
		}
	case <-timer.C:
		c.JSON(http.StatusGatewayTimeout, gin.H{
			"error": "service not responding",
		})
		// 关闭请求
		call.finish()
		s.hp.logger.Warn("request /%s/%s time out", call.service, call.method)
	}
}

func (req *requestInfo) isFinished() bool {
	return atomic.LoadInt32(&req.status) == requestFinished
}

func (req *requestInfo) finish() {
	// 检查避免重复关闭
	if atomic.LoadInt32(&req.status) == requestFinished {
		return
	}

	// 结束请求监听，回收资源
	atomic.StoreInt32(&req.status, requestFinished)
	close(req.done)
}

func (req *requestInfo) doRet(resp *responseInfo) {
	if atomic.LoadInt32(&req.status) != requestWaiting {
		return
	}
	req.done <- resp
}
